package org.wsff.tools.state.event;

import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * AbstractEventMulticaster
 * @author ryan
 * @version Id: AbstractEventMulticaster.java, v 0.1 2022-03-25 17:06 ryan Exp $$
 */
public abstract class AbstractEventMulticaster implements SyncEventMulticaster, EventListenerSupport {

    /** event listener holder */
    private final ListenerHolder listenerHolder = getListenerHolder();

    /** lock */
    private final ReadWriteLock  lock           = new ReentrantReadWriteLock();

    /**
     * registered listener
     *
     * @param listener event listener
     */
    @Override
    public void addListener(BaseEventListener<BaseEvent> listener) {
        try {
            lock.writeLock().lock();
            this.listenerHolder.addListener(listener);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * remove registered listener
     *
     * @param listener event listener
     */
    @Override
    public void removeListener(BaseEventListener<BaseEvent> listener) {
        try {
            lock.writeLock().lock();
            this.listenerHolder.removeListener(listener);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * remove all registered listener
     */
    @Override
    public void removeAllListeners() {
        this.listenerHolder.removeAllListeners();
    }

    /**
     * get all event listener
     *
     * @return all listener
     */
    @Override
    public List<BaseEventListener<BaseEvent>> getAll() {
        return this.listenerHolder.retrieveListeners();
    }

    /**
     * get all support event listener by event
     *
     * @param event event
     * @return all listener
     */
    @Override
    public List<BaseEventListener<BaseEvent>> getAllSupportListeners(BaseEvent event) {
        LinkedList<BaseEventListener<BaseEvent>> allListeners = new LinkedList<>();
        List<BaseEventListener<BaseEvent>> listeners = this.listenerHolder.retrieveListeners();
        for (BaseEventListener<BaseEvent> listener : listeners) {
            // Check whether the event is supported by the listener
            if (listener.isSupportEvent(event)) {
                allListeners.add(listener);
            }
        }
        return allListeners;
    }

    /**
     * get listener holder
     * 
     * @return listener holder
     */
    protected ListenerHolder getListenerHolder() {
        return new DefaultListenerHolder();
    }

    /**
     * Listener Holder
     */
    interface ListenerHolder {

        /**
         * get all event listener
         *
         * @return listeners
         */
        List<BaseEventListener<BaseEvent>> retrieveListeners();

        /**
         * add all event listener
         * 
         * @param listeners event listeners
         */
        void addAllListeners(List<BaseEventListener<BaseEvent>> listeners);

        /**
         * add event listener
         *
         * @param listener event listener
         */
        void addListener(BaseEventListener<BaseEvent> listener);

        /**
         * remove event listener
         *
         * @param listener event listener
         */
        void removeListener(BaseEventListener<BaseEvent> listener);

        /**
         * remove all event listener
         */
        void removeAllListeners();
    }

    /**
     * default listener holder
     */
    static class DefaultListenerHolder implements ListenerHolder {
        /** events container */
        public final Set<BaseEventListener<BaseEvent>> listeners;

        public DefaultListenerHolder() {
            listeners = new LinkedHashSet<>();
        }

        /**
         * get all event listener
         *
         * @return listeners
         */
        @Override
        public List<BaseEventListener<BaseEvent>> retrieveListeners() {
            return new LinkedList<>(this.listeners);
        }

        /**
         * get all event listener
         *
         * @param listeners event listeners
         */
        @Override
        public void addAllListeners(List<BaseEventListener<BaseEvent>> listeners) {
            this.listeners.addAll(listeners);
        }

        /**
         * get all event listener
         *
         * @param listener event listener
         */
        @Override
        public void addListener(BaseEventListener<BaseEvent> listener) {
            this.listeners.add(listener);
        }

        /**
         * remove event listener
         *
         * @param listener event listener
         */
        @Override
        public void removeListener(BaseEventListener<BaseEvent> listener) {
            this.listeners.remove(listener);
        }

        /**
         * remove all event listener
         */
        @Override
        public void removeAllListeners() {
            this.listeners.clear();
        }
    }

}
